package me.chenqiang.cinc.transformers;

import java.util.regex.Matcher;
import java.util.regex.Pattern;

import me.chenqiang.cinc.CincCommons;
import me.chenqiang.cinc.CincCodingException;
import me.chenqiang.cinc.IDType;

public class HMTTravellerIDCodec{
	private static final Pattern PATTERN = Pattern.compile("([HM]?)([A\\d])(\\d{7})");
	private static final String PREFIX_TW = "";
	private static final String PREFIX_HK = "H";
	private static final String PREFIX_MC = "M";
	private static final int PREFIX_HK_ENCODING = 0x64;
	private static final int PREFIX_MC_ENCODING = 0x65;
	private static final int PREFIX_TW_ENCODING = 0x66;
	private static final int PREFIX_BIT_LENGTH = 7;
	
	private HMTTravellerIDCodec() {}

	public static IDType validate(String number) {
		Matcher matcher = PATTERN.matcher(number);
		if(!matcher.matches()) {
			return null;
		}
		switch(matcher.group(1)) {
		case PREFIX_TW:
			return IDType.TAIWAN_RESIDENT_TRAVELLER_ID;
		case PREFIX_HK:
		case PREFIX_MC:
			return IDType.HONGKONG_MACAU_RESIDENT_TRAVELLER_ID;
		default:
			return null;
		}
	}	

	public static String compress(String number) throws CincCodingException {
		Matcher matcher = PATTERN.matcher(number);
		if(!matcher.matches()) {
			throw new CincCodingException(number);
		}
		long prefix = 0;
		switch(matcher.group(1)) {
		case PREFIX_TW:
			prefix = PREFIX_TW_ENCODING;
			break;
		case PREFIX_HK:
			prefix = PREFIX_HK_ENCODING;
			break;
		case PREFIX_MC:
			prefix = PREFIX_MC_ENCODING;
			break;
		default:
			throw new CincCodingException(number);
		}
		
		long serial = Long.parseLong(matcher.group(3));
		if("A".equals(matcher.group(2))) {
			serial += 100000000;
		}
		else {
			serial += 10000000 * Long.parseLong(matcher.group(2));
		}
		
		long encoding = (prefix << (45 - PREFIX_BIT_LENGTH)) | serial;
		
		return CincCommons.toSymbols(encoding, 9, '0');
	}

	public static String decompress(String symbols) throws CincCodingException {
		long encoded = CincCommons.toNumbers(symbols);
		
		int prefix = (int)(encoded >>> (45 - PREFIX_BIT_LENGTH));
		String p;
		switch(prefix) {
		case PREFIX_HK_ENCODING:
			p = PREFIX_HK;
			break;
		case PREFIX_MC_ENCODING:
			p = PREFIX_MC;
			break;
		case PREFIX_TW_ENCODING:
			p = PREFIX_TW;
			break;
		default:
			throw new CincCodingException(symbols);
		}
		
		long serial = encoded & 0x7ffffff;
		if(serial < 100000000) {
			return String.format("%s%08d", p, serial);
		}
		else {
			return String.format("%sA%07d", p, (serial - 100000000));
		}
	}

}
